/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tubes1sister.peerConnection;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import tubes1sister.tools.debug;

/**
 *
 * @author ZAKIY FIRDAUS ALFIKR
 */
public class clientManager implements Runnable {

    //memiliki queue of peer
    public final HashMap<String, String> peer;
    //memiliki queue of pieces yang disimpan adalah index dari pieces beserta nilai hashnya
    public final HashMap<Integer, String> pieces;
    //memiliki clientThread, tempat untuk memanage thread yang ada
    public final HashMap<Integer, clientHandler> clientThread;
    //memiliki current id
    public int id;
    //variable boolean
    public boolean isFinished = false;
    //memiliki apakah sudah mulai
    public boolean isStarting = false;
    //memiliki panjang pieces
    public int panjangFile;
    public int pieceLength;
    //memiliki filename yang akan di download
    public String filename;
    //memiliki nama file asli yang belum di hash
    public String realFilename;
    //memiliki tempat download
    public String downloadDirectory;
    //File yang akan ditulis
    public RandomAccessFile rafDownload;
    public boolean isUpdate = false;
    //konstruktor

    public clientManager(File file, String filename, int pieceLength, String downloadDirectory) throws FileNotFoundException, IOException {
        peer = new HashMap<String, String>();
        pieces = new HashMap<Integer, String>();
        clientThread = new HashMap<Integer, clientHandler>();
        id = 0;
        File downloadedFile;
        this.filename = filename;
        this.pieceLength = pieceLength;
        this.realFilename = file.getName().substring(0, file.getName().length() - 8);
        this.downloadDirectory = downloadDirectory;
        downloadedFile = new File(downloadDirectory + "/" + realFilename);
        if (!downloadedFile.exists()) {
            downloadedFile.createNewFile();
        } else {
            downloadedFile.createNewFile();
        }
        getPieces(file); //termasuk mengeset banyak pieces
        rafDownload = new RandomAccessFile(downloadedFile, "rw");
        rafDownload.setLength(this.panjangFile);
        System.err.print("asd");


    }

    //fungsi untuk mengenerate hashmap peer berdasarkan pesan dari server
    public HashMap<String, String> generatePeerx(String s) {
        HashMap<String, String> hasil;
        hasil = new HashMap<String, String>();
        hasil.put("127.0.0.1", "1234");
        String dummy;
        String ipdummy, portdummy;
        /* Tambahin disini zak */

        /* Process Response yang formatnya kayak trackerresponse.generateMessage*/

        /* End of Tambahin*/
        return hasil;
    }

    //fungsi untuk mengenerate hashmap peer berdasarkan pesan dari server
    public HashMap<String, String> generatePeer(String pesanserver) {
        HashMap<String, String> hasil;
        hasil = new HashMap<String, String>();
        String dummy;
        String dummyas[];
        String listpeers[];
        String ipdummy, portdummy;

        dummyas = pesanserver.split("@");
        dummy = dummyas[5];

        dummyas = dummy.split("peers ");
        dummy = dummyas[1];

        listpeers = dummy.split(" ");

        for (int i = 0; i < listpeers.length; i++) {
            dummy = listpeers[i];
            ipdummy = dummy.split(":")[0];
            portdummy = dummy.split(":")[1];
            if (!hasil.containsKey(ipdummy)) {
                hasil.put(ipdummy, portdummy);
            }
            //System.out.println("IP: "+ipdummy+" || port: "+portdummy);
        }
        return hasil;
    }

    //fungsi untuk menambahkan peer
    public synchronized void addPeer(HashMap<String, String> input) {
        peer.putAll(input);

    }

    //fungsi untuk menambahkan pieces
    public void addPieces(HashMap<Integer, String> input) {
        pieces.putAll(input);
    }

    //fungsi untuk memasangkan antara Peer dan Pieces
    //outputnya adalah class peer
    public synchronized PeerInfo getNextpair() {
        String ip = "", port = "", hash = "";
        int index = -999;
        Collection ipPortCollection = peer.entrySet();
        Collection indexHashCollection = pieces.entrySet();
        Iterator ipIt = ipPortCollection.iterator();
        Iterator indexHash = indexHashCollection.iterator();
        if (ipIt.hasNext()) {
            Entry<String, String> temp = (Entry<String, String>) ipIt.next();
            ip = temp.getKey();
            port = temp.getValue();

        }
        if (indexHash.hasNext()) {
            Entry<Integer, String> temp2 = (Entry<Integer, String>) indexHash.next();
            index = temp2.getKey();
            hash = temp2.getValue();
        }
        if (ip.equals("") || index == -999) {
            return null;
        } else {
            //hapus dari list
            peer.remove(ip);
            pieces.remove(index);
            return new PeerInfo(ip, port, index, hash, this.filename, this.pieceLength, this.panjangFile);
        }

    }

    //berfungsi untuk meng-konekan antar peer
    public void makeConnection(PeerInfo p) {
        clientHandler newHandler = new clientHandler(p, this.id);
        clientThread.put(id, newHandler);
        this.id++;
        newHandler.start();
    }

    //fungsi untuk mengupdate apakah ada thread yang sudah selesai
    public void update() {
        //mencari status dari tiap thread
        isUpdate = true;
        List<Integer> temp = new LinkedList<Integer>();
        synchronized (clientThread) {
            //cek apakah client manager ini sudah selese ato belum
            if (clientThread.isEmpty() && pieces.isEmpty()) {
                try {
                    rafDownload.close();
                } catch (IOException ex) {
                    Logger.getLogger(clientManager.class.getName()).log(Level.SEVERE, null, ex);
                }
                this.isFinished = true;
                return;
            }
            for (Integer key : clientThread.keySet()) {
                clientHandler object = clientThread.get(key);
                //cek apakah sudah selesai atau belum
                synchronized (object) {
                    if (object.isFinished) {
                        //cek status finish nya
                        if (object.statusSelesai != 1) {
                            //berarti ada kesalahan pada saat mendownload
                            //masukkan lagi ke dalam list pieces dan peer
                            peer.put(object.info.ip, object.info.port);
                            pieces.put(object.info.indexPieces, object.info.nilaiHash);
                            //masukkan ke dalam list thread yang akan dihapus

                        } else {
                            try {
                                //berarti berhasil,
                                //tulis pieces yang berhasil dia download ke dalam file
                                rafDownload.seek((long) (object.info.indexPieces * pieceLength));
                                rafDownload.write(object.download);

                            } catch (IOException ex) {
                                peer.put(object.info.ip, object.info.port);
                                pieces.put(object.info.indexPieces, object.info.nilaiHash);
                            }

                        }
                        temp.add(key);
                    }
                }
            }
            //hilangkan dari clientThread thread2 yang sudah selese tapi gagal atau selese dan berhasil
            for (int j = 0; j < temp.size(); j++) {
                clientThread.remove(temp.get(j));
            }
        }
        isUpdate = false;
    }

    private void getPieces(File file) throws FileNotFoundException, IOException {
        try {
            FileInputStream fis = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(fis);
            DataInputStream dis = new DataInputStream(bis);
            int pjg = 0;
            int panjangHash = 40; //panjang karakter hash
            int realLength, piecesLength;
            if (dis.available() != 0) {
                String awal = dis.readLine();
                String[] hasil = awal.split("\\s+");
                realLength = Integer.parseInt(hasil[1]);
                piecesLength = Integer.parseInt(hasil[2]);

                pjg = (int) Math.ceil((double) realLength / piecesLength);
                this.panjangFile = realLength;
                //debug.debugObject("asd");
            }

            String pieceHex = "";
            while (dis.available() != 0) {
                pieceHex = dis.readLine();
            }
            int i = 0;
            while (pieceHex.length() != 0) {
                if (pieceHex.length() < pjg) {
                    pieces.put(i, pieceHex);
                    i++;
                } else {
                    pieces.put(i, pieceHex.substring(0, panjangHash));
                    i++;
                    pieceHex = pieceHex.substring(panjangHash);
                }
                System.out.println(pieces.get(i));
            }
            fis.close();
            bis.close();
            dis.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startDownload() {
        Thread t = new Thread(this);
        t.start();
    }

    public void stopDownload() {
        this.isFinished = true;
    }

    public void run() {
        isStarting = true;
        while (!isFinished) {
            PeerInfo p = getNextpair();
            if (p != null) {
                debug.debugObject("-----------------------------------------------------");
                makeConnection(p);
                debug.debugObject("Membuat Koneksi ke: " + p.ip + "Mendownload Pieces Ke: " + p.indexPieces);
            }
            update();
        }
    }

    public void stop() {
        this.isFinished = true;
    }
}
